package com.vaha.presentation.rest;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.cyber.application.controller.AuthingTokenController;
import com.cyber.domain.constant.HttpResultCode;
import com.cyber.domain.entity.DataResponse;
import com.cyber.domain.entity.IdRequest;
import com.cyber.domain.entity.PagingData;
import com.cyber.domain.entity.Response;
import com.vaha.application.service.*;
import com.vaha.domain.entity.*;
import com.vaha.domain.request.CreateOrderRequest;
import com.vaha.domain.request.OrderRequest;
import com.vaha.domain.request.UpdateOrderRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
public class OrderRest extends AuthingTokenController {

    private final OrderService orderService;

    private final ProductService productService;

    private final ProductItemService productItemService;

    private final UserCardService userCardService;

    private final UserCardManifestService userCardManifestService;

    private final ProductFollowService productFollowService;


    @GetMapping("/user/order/search")
    public Response searchUserOrder(@Valid OrderRequest request) {
        DataResponse<PagingData<Order>> response = new DataResponse<>();
        Order order = request.toEvent(getTenantCode());
        JSONObject user = getUser();
        order.setUserCode(user.getString("code"));
        PagingData<Order> orderPage = orderService.selectPage(order);

        if (!CollectionUtils.isEmpty(orderPage.getData())) {
            List<String> productCodes = orderPage.getData().stream().map(Order::getProductCode).collect(Collectors.toList());
            List<Product> productList = productService.selectByCodes(productCodes);


            if (!CollectionUtils.isEmpty(productList)) {

                Map<String, Product> productMap = productList.stream().collect(Collectors.toMap(Product::getCode, product -> product));

                for (Order orderOne : orderPage.getData()) {
                    Product product = productMap.getOrDefault(orderOne.getProductCode(), null);
                    orderOne.setProduct(product);
                }
            }


            List<String> productItemCodes = orderPage.getData().stream().map(Order::getProductItemCode).collect(Collectors.toList());
            List<ProductItem> productItems = productItemService.selectByCodes(productItemCodes);

            if (!CollectionUtils.isEmpty(productItems)) {

                Map<String, ProductItem> productMap = productItems.stream().collect(Collectors.toMap(ProductItem::getCode, productItem -> productItem));

                for (Order orderOne : orderPage.getData()) {
                    ProductItem productItem = productMap.getOrDefault(orderOne.getProductItemCode(), null);
                    orderOne.setProductItem(productItem);
                }
            }


            List<String> userCardCodes = orderPage.getData().stream().map(Order::getTransctionCardCode).collect(Collectors.toList());
            List<UserCard> userCards = userCardService.selectByCodes(userCardCodes);

            if (!CollectionUtils.isEmpty(userCards)) {

                Map<String, UserCard> productMap = userCards.stream().filter(userCard -> userCard.getUserCode().equals(order.getUserCode())).collect(Collectors.toMap(UserCard::getCode, userCard -> userCard));

                for (Order orderOne : orderPage.getData()) {
                    UserCard userCard = productMap.getOrDefault(orderOne.getTransctionCardCode(), null);
                    orderOne.setUserCard(userCard);
                }
            }


        }

        response.setData(orderPage);
        return response;
    }


    @GetMapping("/user/order")
    public Response selectOneUserOrder(@Valid IdRequest idRequest) {
        DataResponse<Order> response = new DataResponse<>();

        Order order = new Order();
        order.setId(idRequest.getId());
        order.setTenantCode(getTenantCode());
        order = orderService.selectOne(order);

        if (!Objects.isNull(order)) {
            Product product = new Product();
            product.setCode(order.getProductCode());
            product = productService.selectOne(product);
            order.setProduct(product);

            ProductItem productItem = new ProductItem();
            productItem.setCode(order.getProductCode());
            productItem = productItemService.selectOne(productItem);
            order.setProductItem(productItem);

            UserCard userCard = new UserCard();
            userCard.setCode(order.getTransctionCardCode());
            userCard = userCardService.selectOne(userCard);
            order.setUserCard(userCard);
        }

        response.setData(order);
        return response;
    }


    @PostMapping("/user/order")
    public Response saveUserOrder(@RequestBody @Valid CreateOrderRequest request) {
        Order order = request.toEvent(getSessionId(), getTenantCode());

        ProductItem productItem = new ProductItem();
        productItem.setProductCode(request.getProductCode());
        productItem.setCode(request.getProductItemCode());
        productItem.setType(request.getProductType());

        productItem = productItemService.selectOne(productItem);
        if (Objects.isNull(productItem)) {
            return Response.fail(HttpResultCode.RECORD_NOT_EXIST);
        }

        ProductFollow productFollow = new ProductFollow();
        productFollow.setProductItemCode(productItem.getCode());
        productFollow.setProductType(productFollow.getProductType());
        productFollow.setType(10);
        productFollow.setLimit(1);
        productFollow.setOffset(1);
        List<ProductFollow> productFollows = productFollowService.selectByIndex(productFollow);
        if (!CollectionUtils.isEmpty(productFollows)) {
            return Response.success();
        }

        if (productItem.getFree() == 1) {
            return Response.fail(200002, "Products are free without purchase");
        }

        UserCard userCard = new UserCard();
        userCard.setLimit(Integer.MAX_VALUE);
        userCard.setUserCode(getUser().getString("code"));
        List<UserCard> userCards = userCardService.selectByIndex(userCard);
        if (CollectionUtils.isEmpty(userCards)) {
            return Response.fail(HttpResultCode.VALIDATE_ERROR);
        }

        Map<String, BigDecimal> cardBalance = userCards.stream().collect(Collectors.toMap(UserCard::getCode, UserCard::getBalance));
        Map<String, UserCard> userCardMap = userCards.stream().collect(Collectors.toMap(UserCard::getCode, card -> card));
        BigDecimal bonuses = cardBalance.getOrDefault("Bonuses", BigDecimal.ZERO);
        BigDecimal coins = cardBalance.getOrDefault("Coins", BigDecimal.ZERO);


        if (bonuses.compareTo(productItem.getAmount()) >= 0) {
            BigDecimal subtract = bonuses.subtract(productItem.getAmount());
            userCard = userCardMap.get("Bonuses");
            userCard.setBalance(subtract);

        } else if (coins.compareTo(productItem.getAmount()) >= 0) {
            BigDecimal subtract = coins.subtract(productItem.getAmount());
            userCard = userCardMap.get("Coins");
            userCard.setBalance(subtract);
        } else {
            return Response.fail(200001, "not sufficient funds");
        }

        UserCardManifest userCardManifest = new UserCardManifest();
        userCardManifest.toEvent(userCard, order.getId(), 2,getTenantCode());
        userCardManifest.setTransctionName(productItem.getCode()+"-"+productItem.getName());
        userCardManifest.setFromAmount(productItem.getAmount());
        userCardManifest.setTransctionSummary(String.format("Self unlocked %s pay %s %s",productItem.getCode(),productItem.getAmount(),userCard.getCode()));

        userCardManifestService.save(userCardManifest);

        productFollow = new ProductFollow();
        productFollow.toEvent(productItem, userCard.getUserCode(), 10,getTenantCode());
        productFollowService.save(productFollow);

        order.setCode(productItem.getCode() + "-" + RandomUtil.randomString(8));
        order.setTransctionCurrency(userCard.getCurrency());
        order.setTransctionCardCode(userCard.getCode());
        order.setTransctionAmount(productItem.getAmount());
        order.setUserCode(userCard.getUserCode());
        order.setProjectCode("vaha");
        order.setExpireDate("2099-12-31");
        int result = orderService.save(order);
        userCardService.updateById(userCard);
        if (result < 1) {
            return Response.fail(HttpResultCode.SERVER_ERROR);
        }

        return Response.success();
    }

    @GetMapping("/admin/order/search")
    public Response searchOrder(@Valid OrderRequest request) {
        DataResponse<PagingData<Order>> response = new DataResponse<>();
        Order order = request.toEvent(getTenantCode());
        PagingData<Order> orderPage = orderService.selectPage(order);
        response.setData(orderPage);
        return response;
    }


    @GetMapping("/admin/order")
    public Response selectOneOrder(@Valid IdRequest idRequest) {
        DataResponse<Order> response = new DataResponse<>();

        Order order = new Order();
        order.setId(idRequest.getId());
        order.setTenantCode(getTenantCode());
        order = orderService.selectOne(order);

        response.setData(order);
        return response;
    }

    @PostMapping("/admin/order")
    public Response saveOrder(@RequestBody @Valid CreateOrderRequest request) {
        Order order = request.toEvent(getSessionId(), getTenantCode());

        int result = orderService.save(order);
        if (result < 1) {
            return Response.fail(HttpResultCode.SERVER_ERROR);
        }
        return Response.success();
    }

    @PutMapping("/admin/order")
    public Response updateOrder(@RequestBody @Valid UpdateOrderRequest request) {
        Order order = request.toEvent(getSessionId(), getTenantCode());
        int result = orderService.updateById(order);
        if (result < 1) {
            return Response.fail(HttpResultCode.SERVER_ERROR);
        }
        return Response.success();
    }

    @DeleteMapping("/admin/order")
    public Response deleteOrder(@Valid IdRequest idRequest) {
        Order order = new Order();
        order.setId(idRequest.getId());

        order.setTenantCode(getTenantCode());
        order.setUpdator(getSessionId());
        order.setUpdateTime(new Date());

        int result = orderService.deleteById(order);
        if (result < 1) {
            return Response.fail(HttpResultCode.SERVER_ERROR);
        }
        return Response.success();
    }
}
